package cn.com.bluemoon.daps.standard.service.impl;

import cn.com.bluemoon.daps.common.constant.DapConstant;
import cn.com.bluemoon.daps.common.enums.BmStatus;
import cn.com.bluemoon.daps.common.exception.DapException;
import cn.com.bluemoon.daps.common.toolkit.BmAssetUtils;
import cn.com.bluemoon.daps.common.toolkit.TreeUtils;
import cn.com.bluemoon.daps.standard.entity.DapSystemCategory;
import cn.com.bluemoon.daps.standard.mapper.DapSystemCategoryMapper;
import cn.com.bluemoon.daps.standard.service.DapSystemCategoryService;
import cn.com.bluemoon.daps.standard.service.RowDeletePredicate;
import cn.com.bluemoon.daps.standard.service.TopicCategoryDeletePredicate;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;

import java.io.Serializable;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 标准-分类表 服务实现类
 * </p>
 *
 * @author admin
 * @since 2020-08-03
 */
@Slf4j
@Service
@CacheConfig(cacheNames = DapConstant.DapStandard.CACHE_NAME)
public class DapSystemCategoryServiceImpl extends ServiceImpl<DapSystemCategoryMapper, DapSystemCategory> implements DapSystemCategoryService {

    /** 定义分类级别多少 */
    private static final int CATEGORY_MAX_LEVEL = 3;

    @Autowired
    private TopicCategoryDeletePredicate[] topicCategoryDeletePredicates;

    /**
     * 获取当前主题分类的名称和id的映射关系
     * tuple 1 为第一个 主题-id map
     * tuple 2 为第二个 分类-id map
     */
    @Override
    public Tuple2<Map<String, String>, Map<String, String>> dbTopicAndCategoryTuple() {
        List<DapSystemCategory> list = list(Wrappers.lambdaQuery(new DapSystemCategory()).eq(DapSystemCategory::getBmStatus, BmStatus.ON));
        Map<String, String> dbTopicCodeNameAndIdMap = list.stream().filter(dapSystemCategory -> 1 == dapSystemCategory.getTopic()).collect(Collectors.toMap(DapSystemCategory::getCodeName, DapSystemCategory::getId, (a, b) -> b));
        Map<String, String> dbCategoriesCodeNameAndIdMap = list.stream().filter(dapSystemCategory -> 1 != dapSystemCategory.getTopic()).collect(Collectors.toMap(DapSystemCategory::getCodeName, DapSystemCategory::getId, (a, b) -> b));
        log.info("初始化系统主题数：{}个，分类：{}个", dbTopicCodeNameAndIdMap.size(), dbCategoriesCodeNameAndIdMap.size());
        Tuple2<Map<String, String>, Map<String, String>> of = Tuples.of(dbTopicCodeNameAndIdMap, dbCategoriesCodeNameAndIdMap);
        return of;
    }

    /**
     * @param type 1 topic 0 category 其他为获取主题和分类
     */
    @Override
//    @Cacheable(keyGenerator = DapConstant.CACHE_KEY_GENERATOR)
    public List<DapSystemCategory> systemTopicAndCategories(String type) {
        List<DapSystemCategory> systemCategories = list(Wrappers.<DapSystemCategory>lambdaQuery().eq(DapSystemCategory::getBmStatus, BmStatus.ON));
        // topic + level1
        List<DapSystemCategory> topicTrees;
        if ("1".equals(type)) {
            List<DapSystemCategory> topics = systemCategories.stream()
                    .filter(s -> s.getTopic() != null && s.getTopic().equals(1))
                    .collect(Collectors.toList());
            topicTrees = TreeUtils.createTrees(topics, "id",
                    "parentId",
                    "nextNodes", null, null);
        } else if ("0".equals(type)) {
            topicTrees = systemCategories.stream().filter(s -> s.getTopic() == null || s.getTopic().equals(0)).collect(Collectors.toList());
            topicTrees = TreeUtils.createTrees(topicTrees, "id",
                    "parentId",
                    "nextNodes", null, null);
        } else {
            topicTrees = TreeUtils.createTrees(systemCategories, "id",
                    "parentId",
                    "nextNodes", null, null);
        }
        return topicTrees;
    }

    @Override
    @Cacheable(keyGenerator = DapConstant.CACHE_KEY_GENERATOR)
    public DapSystemCategory getById(Serializable id) {
        return super.getById(id);
    }

    @Override
    @Cacheable(keyGenerator = DapConstant.CACHE_KEY_GENERATOR)
    public List<DapSystemCategory> list(Wrapper<DapSystemCategory> queryWrapper) {
        return super.list(queryWrapper);
    }

    @Override
//    @Cacheable(keyGenerator = DapConstant.CACHE_KEY_GENERATOR)
    public List<DapSystemCategory> findSystemCategoriesByParentId(String parentId) {
        List<DapSystemCategory> list = list(Wrappers.lambdaQuery(new DapSystemCategory()).eq(DapSystemCategory::getBmStatus, BmStatus.ON));
        if (list == null || list.isEmpty()) {
            return Collections.emptyList();
        }
        Map<Object, List<DapSystemCategory>> keyMap = list.stream().filter(d -> 1 != d.getTopic()).collect(Collectors.groupingBy(k -> k.getParentId()));
        DapSystemCategory root = getById(parentId);
        if (root == null) {
            return Collections.emptyList();
        }
        final Function<DapSystemCategory, List<DapSystemCategory>> function = dapSystemCategory -> {
            List<DapSystemCategory> category = keyMap.get(dapSystemCategory.getId());
            return category == null ? Collections.emptyList() : category;
        };
        // 目前只取分类的两层，没有用递归
        List<DapSystemCategory> category1 = function.apply(root);
        List<DapSystemCategory> category2 = category1.stream().flatMap(s -> function.apply(s).stream()).collect(Collectors.toList());
        category1.addAll(category2);
        List<DapSystemCategory> category3 = category2.stream().flatMap(s -> function.apply(s).stream()).collect(Collectors.toList());
        category1.addAll(category3);
        return category1;
    }

    @Override
    @Cacheable(keyGenerator = DapConstant.CACHE_KEY_GENERATOR)
    public Optional<DapSystemCategory> findOneByCodeName(String codeName, boolean isTopic) {
        LambdaQueryWrapper<DapSystemCategory> where = Wrappers.lambdaQuery(new DapSystemCategory())
                .eq(DapSystemCategory::getCodeName, codeName)
                .eq(DapSystemCategory::getBmStatus, BmStatus.ON)
                .eq(DapSystemCategory::getTopic, isTopic ? 1 : 0)
                .last("limit 1");
        DapSystemCategory dapSystemCategory = getOne(where);
        return Optional.ofNullable(dapSystemCategory);
    }

    @Override
//    @Cacheable(keyGenerator = DapConstant.CACHE_KEY_GENERATOR)
    public ImmutableMap<String, Map<String, List<DapSystemCategory>>> getTopicsAndCategories() {
        List<DapSystemCategory> trees1 = list(bmLambdaQuery());
        // 递归
        // pid + nodes
        Map<String, List<DapSystemCategory>> topics1 = Maps.newHashMap();
        Map<String, List<DapSystemCategory>> topics2 = Maps.newHashMap();
        List<DapSystemCategory> topics = trees1.stream().filter(d -> 1 == d.getTopic()).collect(Collectors.toList());
        topics1.put("1", topics);
        topics2.put("2", topics);
        Map<String, List<DapSystemCategory>> categories1 = Maps.newHashMap();
        Map<String, List<DapSystemCategory>> categories2 = Maps.newHashMap();
        Map<String, List<DapSystemCategory>> categories3 = Maps.newHashMap();
        for (DapSystemCategory category : trees1) {
            if (category.getTopic() == 0 || category.getTopic() == null) {
                if (category.getSubLevel() == 1) {
                    setTopicOrCategory(categories1, category);
                } else if (category.getSubLevel() == 2) {
                    setTopicOrCategory(categories2, category);
                } else if (category.getSubLevel() == 3) {
                    setTopicOrCategory(categories3, category);
                }
            }
        }
        ImmutableMap<String, Map<String, List<DapSystemCategory>>> build = ImmutableMap.<String, Map<String, List<DapSystemCategory>>>builder()
                .put(DapConstant.DapStandard.SYSTEM_TOPIC1_NAME, topics1)
                .put(DapConstant.DapStandard.SYSTEM_TOPIC2_NAME, topics2)
                .put(DapConstant.DapStandard.SYSTEM_CATEGORY1_NAME, categories1)
                .put(DapConstant.DapStandard.SYSTEM_CATEGORY2_NAME, categories2)
                .put(DapConstant.DapStandard.SYSTEM_CATEGORY3_NAME, categories3)
                .build();
        return build;
    }

    /**
     * 删除
     *
     * @param isLogic 逻辑删除？
     */
    @Override
    public boolean deleteById(String id, boolean isLogic) {
        DapSystemCategory category = getById(id);
        BmAssetUtils.isFalse(category == null, "获取分类信息失败");
        if (isLogic) {
            category.setBmStatus(BmStatus.DELETE);
            return category.updateById();
        } else {
            return removeById(id);
        }
    }

    /**
     * 默认取第二层
     *
     * @param level 2 为主题的第二层
     */
    @Override
    public List<DapSystemCategory> findTopicsByLevel(Integer level) {
        //noinspection AliControlFlowStatementWithoutBraces
        if (level == null) {
            level = 0;
        }
        DapSystemCategory dapSystemCategory = new DapSystemCategory();
        dapSystemCategory.setSubLevel(level);
        dapSystemCategory.setTopic(1);
        dapSystemCategory.setBmStatus(BmStatus.ON);
        return list(Wrappers.query(dapSystemCategory));
    }

    @Override
    public DapSystemCategory saveOrUpdateSysCategory(DapSystemCategory sysCategory) {
        // 判断分类pid是否存在
        String parentId = sysCategory.getParentId();
        int isTopic = 1;
        if (parentId != null) {
            isTopic = 0;
            getByIdAsset(parentId, "请选择左树节点");
        }
        sysCategory.setTopic(isTopic);
        if (sysCategory.getId() == null) {
            return saveWithPredicate(sysCategory);
        } else {
            return updateWithPredicate(sysCategory);
        }
    }

    @Override
    public boolean deleteTopicCategoryById(String topicCategoryId) {
        checksysCategory(topicCategoryId);
        boolean remove;
        // 增加业务逻辑判断是否能删除改业务线
        if (topicCategoryDeletePredicates != null) {
            for (TopicCategoryDeletePredicate deletePredicate : topicCategoryDeletePredicates) {
                RowDeletePredicate.Result<String> predicate = deletePredicate.predicate(topicCategoryId);
                if (predicate != null && !predicate.isCanDelete()) {
                    throw new DapException(predicate.getMessage());
                }
            }
        }
        // 递归删除当前节点和叶子节点
        List<DapSystemCategory> nodes = new ArrayList<>();
        findNextNodeByCurrentId(nodes, topicCategoryId);
        remove = removeByIds(nodes.stream().map(DapSystemCategory::getId).collect(Collectors.toList()));
        return remove;
    }

    private void findNextNodeByCurrentId(List<DapSystemCategory> nodes, String currentId) {
        DapSystemCategory currNode = getById(currentId);
        if (currNode != null) {
            nodes.add(currNode);
            List<DapSystemCategory> nodesByPid = findNodesByPid(currNode.getId());
            if (nodesByPid != null && !nodesByPid.isEmpty()){
                for (DapSystemCategory node : nodesByPid) {
                    findNextNodeByCurrentId(nodes, node.getId());
                }
            }
        }
    }

    private List<DapSystemCategory> findNodesByPid(String pid) {
        return list(bmLambdaQuery().eq(DapSystemCategory::getParentId, pid));
    }

    private DapSystemCategory updateWithPredicate(DapSystemCategory sysCategoryBo) {
        String sysCategoryId = sysCategoryBo.getId();
        checksysCategory(sysCategoryId);
        LambdaQueryWrapper<DapSystemCategory> exits = bmLambdaQuery().ne(DapSystemCategory::getId, sysCategoryId).and(wrapper ->
                wrapper.eq(DapSystemCategory::getCode, sysCategoryBo.getCode()).or().eq(DapSystemCategory::getCodeName, sysCategoryBo.getCodeName()))
                // 分类就要针对当前编辑的层级进行检查 主题则全局
                .eq(sysCategoryBo.getTopic() == 0, DapSystemCategory::getParentId, sysCategoryBo.getParentId())
                .eq(sysCategoryBo.getTopic() == 1, DapSystemCategory::getTopic, 1);
        return saveOrUpdateWithPredicate(sysCategoryBo, exits);
    }

    private DapSystemCategory checksysCategory(String sysCategoryId) {
        DapSystemCategory dbsysCategory = getById(sysCategoryId);
        BmAssetUtils.isTrue(dbsysCategory != null && BmStatus.ON.equals(dbsysCategory.getBmStatus()), "主题或分类不存在");
        return dbsysCategory;
    }

    private DapSystemCategory saveWithPredicate(DapSystemCategory sysCategoryBo) {
        LambdaQueryWrapper<DapSystemCategory> exits = bmLambdaQuery()
                .and(wrapper ->
                        wrapper.eq(DapSystemCategory::getCode, sysCategoryBo.getCode())
                                .or()
                                .eq(DapSystemCategory::getCodeName, sysCategoryBo.getCodeName())
                )
                // 分类就要针对当前编辑的层级进行检查 主题则全局
                .eq(sysCategoryBo.getTopic() == 0, DapSystemCategory::getParentId, sysCategoryBo.getParentId())
                .eq(sysCategoryBo.getTopic() == 1, DapSystemCategory::getTopic, 1);
        return saveOrUpdateWithPredicate(sysCategoryBo, exits);
    }

    /**
     * 新增、修改
     * 带判断（是否有物理关系、是否分类超过3级）
     */
    private DapSystemCategory saveOrUpdateWithPredicate(DapSystemCategory sysCategoryBo, LambdaQueryWrapper<DapSystemCategory> exits) {
        if (sysCategoryBo == null){
            throw new DapException("新增/修改主题分类不可为空");
        }
        // 反递归（从pid往根节点）
        int level = 0;
        if (sysCategoryBo.getParentId() != null) {
            DapSystemCategory tmpPid = sysCategoryBo;
            while (tmpPid.getParentId() != null && tmpPid.getTopic() == 0) {
                tmpPid = getById(tmpPid.getParentId());
                if (level >= CATEGORY_MAX_LEVEL) {
                    throw new DapException("分类最多" + CATEGORY_MAX_LEVEL + "级");
                }
                level++;
            }
        }
        List<DapSystemCategory> hadsysCategorys = list(exits);
        if (hadsysCategorys.isEmpty()) {
            sysCategoryBo.setSubLevel(level);
            saveOrUpdate(sysCategoryBo);
            return sysCategoryBo;
        } else {
            throw new DapException("主题/分类名称或编码已存在");
        }
    }

    private void setTopicOrCategory(Map<String, List<DapSystemCategory>> topics1, DapSystemCategory category) {
        List<DapSystemCategory> nodes = topics1.get(category.getParentId());
        if (nodes == null) {
            topics1.put(category.getParentId(), Lists.newArrayList(category));
        } else {
            nodes.add(category);
        }
    }
}
